/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022.
 * Description: support for RTOS_MEM_HUGEPAGE_SHARED_LIB
 * Author: liusongtang
 * Create: 2022-08-26
 */

#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/hugetlb.h>
#include <linux/rtos_mm_types.h>
#include "internal.h"

static bool can_hugepagefile_unlink(struct vm_area_struct *vma)
{
	pte_t pte;
	pte_t *ptep;
	struct page *page;
	unsigned long addr = vma->vm_start;

	for (; addr < vma->vm_end; addr += HPAGE_SIZE) {
		ptep = huge_pte_offset(vma->vm_mm, addr, HPAGE_SIZE);
		if (!ptep)
			continue;

		pte = huge_ptep_get(ptep);
		if (huge_pte_none(pte))
			continue;

		page = pte_page(pte);
		if (PageAnon(page))
			continue;
		if (page_mapcount(page) > 1)
			return false;
	}

	return true;
}

void unlink_hugepage_file(struct mm_struct *mm)
{
	int err;
	char *path;
	char *buf;
	struct vm_area_struct *vma;
	struct rtos_mm_struct *rtos_mm = mm_to_rtos_mm(mm);
	bool flag;

	if (!rtos_mm->hugepage_shared)
		return;

	buf = __getname();
	if (!buf)
		return;

	vma = mm->mmap;
	flag = true;
	for (; vma; vma = vma->vm_next) {
		if (!vma->vm_file || !is_vm_hugetlb_page(vma))
			continue;
		if (!(vma->rtos_vm_flags & RTOS_VM_HUGEPAGE_SHARED_LIB))
			continue;

		if (!can_hugepagefile_unlink(vma))
			continue;
		path = d_path(&vma->vm_file->f_path, buf, PATH_MAX);
		if (IS_ERR(path)) {
			flag = false;
			pr_warn("hugeso: fetch hugefile path failed!\n");
		} else {
			err = do_unlinkat(AT_FDCWD, getname_kernel(path));
			/* If hugepage file has been unlinked, do_unlinkat will return ENOENT */
			if (err && (err != -ENOENT)) {
				flag = false;
				pr_warn("hugeso: unlink may failed! ret=%d\n", err);
			}
		}
	}
	__putname(buf);
	if (flag)
		rtos_mm->hugepage_shared = 0;
}

